package com.spa.application.service.member;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.spa.application.command.member.MemberEnterOrderPrecontractCommand;
import com.spa.application.command.member.MemberOrderAddCommand;
import com.spa.application.command.member.MemberOrderExtAddCommand;
import com.spa.application.command.member.MemberOrderPrecontractCommand;
import com.spa.application.command.member.OrderCancelCommand;
import com.spa.application.command.member.OrderExtPayCommand;
import com.spa.application.command.member.OrderPayCommand;
import com.spa.application.command.wx.WXOrderCommand;
import com.spa.application.command.wx.WXRefundCommand;
import com.spa.application.dto.PageResult;
import com.spa.application.dto.member.MemberAppointmentTimeDTO;
import com.spa.application.dto.member.MemberOrderDTO;
import com.spa.application.dto.member.MemberOrderDetailDTO;
import com.spa.application.dto.member.MemberOrderExtDTO;
import com.spa.application.dto.member.MemberOrderPrecontractDTO;
import com.spa.application.dto.wx.WXPayDTO;
import com.spa.application.query.member.MemberMassagistTimeQuery;
import com.spa.application.query.member.MemberOrderDetailsQuery;
import com.spa.application.query.member.MemberOrderQuery;
import com.spa.application.service.massagist.MassagistOrderApplicationService;
import com.spa.domain.entity.*;
import com.spa.domain.service.*;
import com.spa.infrastructure.common.CommonCode;
import com.spa.infrastructure.common.KeyConstant;
import com.spa.infrastructure.enums.dict.base.SourceTypeEnum;
import com.spa.infrastructure.enums.dict.base.WhetherTypeEnum;
import com.spa.infrastructure.enums.dict.massagist.MassagistStatusEnum;
import com.spa.infrastructure.enums.dict.order.OrderOperStatusEnum;
import com.spa.infrastructure.enums.dict.order.OrderStatusEnum;
import com.spa.infrastructure.enums.dict.order.OrderSubStatusEnum;
import com.spa.infrastructure.exception.SpaException;
import com.spa.infrastructure.mapper.OrderMapperStruct;
import com.spa.infrastructure.repository.condition.OrderExtCondition;
import com.spa.infrastructure.repository.condition.OrderInfoCondition;
import com.spa.infrastructure.repository.condition.OrderTransferCondition;
import com.spa.infrastructure.repository.po.OrderExtPO;
import com.spa.infrastructure.repository.po.OrderInfoPO;
import com.spa.infrastructure.repository.service.SysConfigurationRepositoryService;
import com.spa.infrastructure.service.gaode.GaodeService;
import com.spa.infrastructure.service.mp.FlowEnum;
import com.spa.infrastructure.service.mp.Producer;
import com.spa.infrastructure.service.sms.ApplicationConfiguration;
import com.spa.infrastructure.service.sms.SmsProvider;
import com.spa.infrastructure.service.sms.huaxin.HuaXinUtil;
import com.spa.infrastructure.service.wx.WXPayServiceV3;
import com.spa.infrastructure.service.wx.config.ChatConfig;
import com.spa.infrastructure.service.wx.result.PaySuccessResult;
import com.spa.infrastructure.service.yunxin.VoiceSenderService;
import com.spa.infrastructure.util.*;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 用户订单 application
 *
 * @author: lig
 * @date: 2023/12/9
 */
@Service
@Slf4j
public class MemberOrderApplicationService {

    @Resource
    private OrderService orderService;
    @Resource
    private MassagistService massagistService;
    @Resource
    private MassageService massageService;
    @Resource
    private AddressService addressService;
    @Resource
    private GaodeService gaodeService;

    @Resource
    private UserTicketService userTicketService;

    @Resource
    private FeeUtil feeUtil;

    @Autowired
    private ChatConfig chatConfig;

    @Autowired
    private WXPayServiceV3 wxPayServiceV3;

    @Resource
    private TradeService tradeService;

    @Resource
    private EnvUtil envUtil;

    @Autowired
    private Producer producer;

    @Autowired
    private OrderTransferService orderTransferService;

    @Autowired
    private MassagistOrderApplicationService massagistOrderApplicationService;

    @Value("${spa.test.order.price}")
    private Integer testOrderPrice;

    @Value("${spa.test.travel.price}")
    private Integer testTravelPrice;

    @Autowired
    private ProfitService profitService;

    @Resource
    private RedisUtil redisUtil;

    @Autowired
    private ShopService shopService;

    @Autowired
    private ShopUserService shopUserService;

    @Autowired
    private ApplicationConfiguration smsConfig;

    @Autowired
    private VoiceSenderService voiceSenderService;

    @Autowired
    private CouponService couponService;

    @Resource
    private SmsProvider smsProvider;

    @Autowired
    private SysConfigurationRepositoryService sysConfigurationRepositoryService;

    /**
     * 立即预约
     *
     * @author: lig
     * @date: 2023-12-13
     */
    public MemberOrderPrecontractDTO precontract(MemberOrderPrecontractCommand command) {
        MemberOrderPrecontractDTO dto = new MemberOrderPrecontractDTO();
        //获取技师信息
        Massagist massagist = massagistService.infoById(command.getMassagistId());
        Assert.isTrue(!Objects.equals(massagist.getStatus(), MassagistStatusEnum.$10.getCode()), "技师不是可预约状态");
        long userId = SessionUtil.getUser().getExtUserId();
        //服务项目信息
        MassageServiceEntity serviceEntity = massageService.infoById(command.getServiceId());
        UserAddress address = null;
        if (ObjectUtils.isNotEmpty(command.getAddressId())) {
            if (addressService.isUserAddress(userId, command.getAddressId())) {
                address = addressService.detailsById(command.getAddressId());
            }
        } else {
            //获取登录用户默认地址
            address = addressService.defaultInfo(userId);
        }
        //服务---
        dto.setServiceId(serviceEntity.getId());
        dto.setServiceName(serviceEntity.getName());
        dto.setServiceTime(serviceEntity.getDuration());
        dto.setServicePrice(Convert.toStr(serviceEntity.getPrice(), "0"));
        dto.setServiceRecommend(serviceEntity.getRecommendations());
        dto.setServiceQuantity(Convert.toInt(command.getServiceQuantity(), 1));
        dto.setCover(serviceEntity.getCover());
        dto.setDiscountPrice(Convert.toStr(serviceEntity.getDiscountPrice(), "0"));
        //技师---
        dto.setMassagistId(massagist.getId());
        dto.setMassagistName(massagist.getName());
        if (ObjectUtil.isNotNull(address)) {
            dto.setAddressDefaultName(address.getName());
            dto.setAddressDefaultPhone(address.getPhone());
            dto.setAddressDefault(address.getRegion() + address.getAddress());
            dto.setAddressDefaultId(address.getId());
            BigDecimal distDouble = gaodeService.getDistance(massagist.getLon(), massagist.getLat(), address.getLon()
                    , address.getLat());
            //出行距离
            dto.setDist(OrderUtils.gainDoubleDist(distDouble.doubleValue()).toString());
            //出行费用
            Double travelPrice = Convert.toDouble(feeUtil.calculateFee(distDouble, massagist.getShopId()), 0d);
            dto.setTravelPrice(travelPrice.toString());

            dto.setCouponId(ObjectUtils.isNotEmpty(command.getCouponId()) ? command.getCouponId() : null);
            //优惠券金额
            Integer couponPriceInt = ObjectUtil.isNull(command.getCouponId()) ? 0 :
                    userTicketService.getCouponPrice(command.getCouponId());
            int couponPrice = 0;
            //使用了团购券，不能使用优惠券,更新团购券状态
            if (command.isUseCoupon()) {
                couponPriceInt = 0;
                List<Coupon> coupons = couponService.getCouponsByUserIdAndServiceId(userId, command.getServiceId());
                int size = coupons.size();
                int count = Math.min(size, command.getServiceQuantity());
                couponPrice = count * serviceEntity.getDiscountPrice();
            }
            dto.setCouponPrice(Convert.toStr(couponPriceInt, "0"));
            dto.setServiceTotalPrice(OrderUtils.calcOrderServiceTotal(serviceEntity.getDiscountPrice(),
                    command.getServiceQuantity()).toString());
            int totalPrice = OrderUtils.calcOrderTotal(serviceEntity.getDiscountPrice(),
                    command.getServiceQuantity(), couponPriceInt, travelPrice);
            dto.setTotalPrice(String.valueOf(totalPrice - couponPrice));
        } else {
            dto.setTravelPrice("0");
            dto.setDist("0");
        }
        Shop shop = shopService.getShopById(massagist.getShopId());
        CitiesServed cities = shop.getCity();
        double price = ObjectUtil.isNotNull(cities.getPrice()) && cities.getPrice() != 0 ? cities.getPrice() : FeeUtil.STARTING_PRICE;
        double distance = ObjectUtil.isNotNull(cities.getDistance()) && cities.getDistance() != 0 ? cities.getDistance() : FeeUtil.DEFAULT_DISTANCE;
        double incrementPrice = ObjectUtil.isNotNull(cities.getIncrementPrice()) && cities.getIncrementPrice() != 0 ? cities.getIncrementPrice() : FeeUtil.INCREMENT_PRICE;
        dto.setDistance(distance);
        dto.setPrice(price);
        dto.setIncrementPrice(incrementPrice);
        boolean hasCoupon = couponService.existByUserIdAndServiceId(SessionUtil.getUser().getExtUserId(), serviceEntity.getId());
        dto.setHasCoupon(hasCoupon);
        return dto;
    }

    /**
     * 新增订单  下单
     *
     * @author: lig todo 如果这里传入了不能使用的优惠券ID是不是有风险？
     * @date: 2023/12/6
     */
    @Transactional
    public MemberOrderDTO addOrder(MemberOrderAddCommand command) {
        long userId = SessionUtil.getUser().getExtUserId();
        //黑名单直接返回
        if (sysConfigurationRepositoryService.getValueByKey(KeyConstant.MEMBER_BLACKLIST).contains(String.valueOf(userId))) {
            throw new SpaException("");
        }
        Date appointTime = command.getAppointTime();
        List<LocalDateTime> localDateTimes = OrderUtils.getAvailableTimeList(getDates(command.getMassagistId(), LocalDateTime.of(LocalDate.now(), LocalTime.MIN)));
        if (!OrderUtils.contains(localDateTimes, OrderUtils.toLocalDateTime(appointTime))) {
            throw new SpaException("该时间段已经被预约！");
        }
        //获取技师信息
        Massagist massagist = massagistService.infoById(command.getMassagistId());
        //技师的状态必须是可预约状态 todo 这是业务逻辑 应该上移
        Assert.isTrue(!Objects.equals(massagist.getStatus(), MassagistStatusEnum.$10.getCode()), "技师状态为休息中");
        //地址
        UserAddress address = addressService.detailsById(command.getAddressId());
        Assert.isTrue(ObjectUtils.isNotEmpty(address), "地址不存在");
        MemberOrderDTO dto = new MemberOrderDTO();
        //生成订单
        Long id = orderService.addOrderInfo(command, massagist, address);
        //使用了团购券，不能使用优惠券,更新团购券状态
        if (command.isUseCoupon()) {
            List<Coupon> coupons = couponService.getCouponsByUserIdAndServiceId(userId, command.getServiceId());
            int size = coupons.size();
            int count = Math.min(size, command.getServiceQuantity());
            for (int i = 0; i< count; i++) {
                Coupon coupon = coupons.get(i);
                couponService.useCoupon(coupon.getId(), id);
            }
        }
        dto.setId(id);
        //使用用户优惠券
        if (ObjectUtils.isNotEmpty(command.getCouponId())) {
            userTicketService.delUserTicket(command.getCouponId(), userId);
        }
        return dto;
    }

    /**
     * 新增扩展订单  加钟
     *
     * @author: lig
     * @date: 2023-12-09
     */
    @Transactional
    public MemberOrderExtDTO addOrderExt(MemberOrderExtAddCommand command) {
        OrderInfo orderInfo = orderService.getOrderInfoById(command.getOrderId(), OrderService.OrderInfoPlug.empty());
        Assert.isTrue(orderInfo.getStatus().equals(OrderStatusEnum.$30.getCode()), "该订单状态不可加钟");
        MemberOrderExtDTO dot = new MemberOrderExtDTO();
        long id = orderService.addOrderInfoExt(command);
        dot.setId(id);
        dot.setOrderId(orderInfo.getId());
        return dot;
    }

    /**
     * 分页查询订单列表
     *
     * @param query 查询条件
     * @return 订单列表
     */
    public PageResult<MemberOrderDTO> page(MemberOrderQuery query) {
        Long userId = SessionUtil.getUser().getExtUserId();
        OrderInfoCondition orderCondition = OrderMapperStruct.INSTANCE.query2Condition(query, userId);
        OrderService.OrderInfoPlug plug = OrderService.OrderInfoPlug.base();
        plug.setTicket(true);
        plug.setEvaluate(true);
        Page<OrderInfo> orderInfoPage = orderService.pageByCondition(orderCondition, plug);
        List<OrderInfo> orderListDTOList = orderInfoPage.getRecords();
        List<Long> orderIds = orderListDTOList.stream().map(OrderInfo::getId).toList();
        OrderTransferCondition condition = new OrderTransferCondition();
        condition.setOrderIds(orderIds);
        condition.setAscCreateTime("asc");
        List<OrderTransfer> orderTransfers = orderTransferService.listByCondition(condition);
        Map<Long, OrderTransfer> map = orderTransfers.stream().collect(Collectors.toMap(OrderTransfer::getOrderId, Function.identity(), (k1, k2) -> k1));
        orderListDTOList.forEach(orderListDTO -> {
            OrderTransfer orderTransfer = map.getOrDefault(orderListDTO.getId(), null);
            if (ObjectUtil.isNotEmpty(orderTransfer)) {
                orderListDTO.setMassagistName(orderTransfer.getAppointMassagistName());
                orderListDTO.setMassagistId(orderTransfer.getAppointMassagistId());
            }
        });
        return OrderMapperStruct.INSTANCE.entity2MemberDto4Page(orderInfoPage);
    }

    /**
     * 取消订单
     *
     * @author: lig
     * @date: 2023-12-09
     */
    public boolean cancelOrder(OrderCancelCommand command) {
        String lockKey = "cancelOrder：lock:" + command.getId();
        String id = UUID.fastUUID().toString(true);
        try {
            boolean lockFlag = redisUtil.getLock(lockKey, id);
            if (!lockFlag) {
                throw new SpaException("操作频繁！");
            }
            OrderService.OrderInfoPlug plug = OrderService.OrderInfoPlug.base();
            plug.setTicket(true);
            plug.setEvaluate(true);
            OrderInfo orderInfo = orderService.getOrderInfoById(command.getId(), plug);
            if (OrderOperStatusEnum.$40.getCode().equals(orderInfo.getOrderOperStatus())) {
                throw new SpaException("取消失败，技师已经开始服务了哦");
            }
            Assert.isTrue((orderInfo.getStatus().equals(OrderStatusEnum.$10.getCode()) || orderInfo.getStatus().equals(OrderStatusEnum.$20.getCode()) || orderInfo.getStatus().equals(OrderStatusEnum.$30.getCode())), " 该状态不可以取消");
            orderInfo.setCancelReason(command.getCancelReason());
            orderInfo.setSubStatus(orderInfo.getStatus().equals(OrderStatusEnum.$10.getCode()) ? OrderSubStatusEnum.$10.getCode() : OrderSubStatusEnum.$20.getCode());
            boolean boo = orderService.cancelOrder(orderInfo);
            if (boo) {
                //发送订单取消消息
                producer.send(FlowEnum.$CANCEL, orderInfo.getId());
                //使用了团购券，需要更新状态
                List<Coupon> coupons = couponService.getCouponsByOrderId(command.getId());
                coupons.forEach(coupon -> {
                    couponService.returnCoupon(coupon.getId());
                });
                int servicePrice = orderInfo.getOrderPrice() - orderInfo.getTravelPrice();
                int travelPrice = orderInfo.getTravelPrice();
                if (CollUtil.isNotEmpty(coupons)) {
                    if (Objects.equals(orderInfo.getStatus(), OrderStatusEnum.$20.getCode())) {
                        int refundPrice = travelPrice;
                        if (envUtil.isTest()) {
                            refundPrice = testOrderPrice;
                        }
                        refund(command.getSource(), refundPrice, orderInfo.getOrderCode());
                        cancelOrderTransfer(orderInfo);
                    } else if (Objects.equals(orderInfo.getStatus(), OrderStatusEnum.$30.getCode())) {
                        //已经接单 技师未出发 用户取消 退车费
                        if (Objects.equals(orderInfo.getOrderOperStatus(), OrderOperStatusEnum.$10.getCode())) {
                            int refundPrice = travelPrice;
                            if (envUtil.isTest()) {
                                refundPrice = testOrderPrice;
                            }
                            refund(command.getSource(), refundPrice, orderInfo.getOrderCode());
                            massagistOrderApplicationService.optionMassagistStatus(orderInfo.getMassagistId());
                        } else if (Objects.equals(orderInfo.getOrderOperStatus(), OrderOperStatusEnum.$20.getCode())) {
                            //接单 已经出发 出行费不退
                            //此种情况需要将车费给到技师
                            profitService.addTravelPrice(orderInfo.getShopId(), orderInfo.getMassagistId(), orderInfo.getId(), travelPrice);
                            massagistOrderApplicationService.optionMassagistStatus(orderInfo.getMassagistId());
                        }else if (Objects.equals(orderInfo.getOrderOperStatus(), OrderOperStatusEnum.$30.getCode())) {
                            //接单 已经到达 出行费不退
                            //此种情况需要将车费给到技师
                            profitService.addTravelPrice(orderInfo.getShopId(), orderInfo.getMassagistId(), orderInfo.getId(), travelPrice);
                            massagistOrderApplicationService.optionMassagistStatus(orderInfo.getMassagistId());
                        }
                    }
                } else {
                    if (Objects.equals(orderInfo.getStatus(), OrderStatusEnum.$20.getCode())) {
                        int refundPrice = orderInfo.getOrderPrice();
                        //待接单 用户取消；全部退还
                        if (envUtil.isTest()) {
                            refundPrice = testOrderPrice;
                        }
                        refund(command.getSource(), refundPrice, orderInfo.getOrderCode());
                        cancelOrderTransfer(orderInfo);
                    } else if (Objects.equals(orderInfo.getStatus(), OrderStatusEnum.$30.getCode())) {
                        //已经接单 技师未出发 用户取消 只退90%的项目费 和车费
                        if (Objects.equals(orderInfo.getOrderOperStatus(), OrderOperStatusEnum.$10.getCode())) {
                            int refundPrice = (int) (servicePrice * 0.9) + travelPrice;
                            if (envUtil.isTest()) {
                                refundPrice = testOrderPrice;
                            }
                            refund(command.getSource(), refundPrice, orderInfo.getOrderCode());
                            profitService.addCancelFee4Admin(orderInfo.getShopId(), orderInfo.getMassagistId(), orderInfo.getId(), (int) (servicePrice * 0.1));
                            massagistOrderApplicationService.optionMassagistStatus(orderInfo.getMassagistId());
                        } else if (Objects.equals(orderInfo.getOrderOperStatus(), OrderOperStatusEnum.$20.getCode())) {
                            //接单 已经出发 出行费不退 只退80%的项目费
                            int refundPrice = (int) (servicePrice * 0.8);
                            if (envUtil.isTest()) {
                                refundPrice = testOrderPrice - testTravelPrice;
                            }
                            //此种情况需要将车费给到技师
                            profitService.addTravelPrice(orderInfo.getShopId(), orderInfo.getMassagistId(), orderInfo.getId(), travelPrice);
                            profitService.addCancelFee4Admin(orderInfo.getShopId(), orderInfo.getMassagistId(), orderInfo.getId(), (int) (servicePrice * 0.2));
                            refund(command.getSource(), refundPrice, orderInfo.getOrderCode());
                            massagistOrderApplicationService.optionMassagistStatus(orderInfo.getMassagistId());
                        }else if (Objects.equals(orderInfo.getOrderOperStatus(), OrderOperStatusEnum.$30.getCode())) {
                            //接单 已经到达 出行费不退 只退50%的项目费，使用团购券的只扣车费
                            int refundPrice = (int) (servicePrice * 0.5) ;
                            if (envUtil.isTest()) {
                                refundPrice = testOrderPrice - testTravelPrice;
                            }
                            //此种情况需要将车费给到技师
                            profitService.addTravelPrice(orderInfo.getShopId(), orderInfo.getMassagistId(), orderInfo.getId(), travelPrice);
                            profitService.addCancelFee4Admin(orderInfo.getShopId(), orderInfo.getMassagistId(), orderInfo.getId(), (int) (servicePrice * 0.5));
                            refund(command.getSource(), refundPrice, orderInfo.getOrderCode());
                            massagistOrderApplicationService.optionMassagistStatus(orderInfo.getMassagistId());
                        }
                    }
                }
                if (ObjectUtils.isNotEmpty(orderInfo.getTicketId())) {
                    //退还优惠券
                    userTicketService.returnUserTicket(orderInfo.getTicketId());
                }
                redisUtil.delete(RedisKeyUtil.getAutoTransferKey(String.valueOf(orderInfo.getId())));
                redisUtil.remove(RedisKeyUtil.getAutoTransferGlobalKey(), String.valueOf(orderInfo.getId()));
            }
            return boo;
        } finally {
            redisUtil.releaseLock(lockKey, id);
        }
    }

    /**
     * 取消订单转单
     *
     * @param orderInfo
     */
    private void cancelOrderTransfer(OrderInfo orderInfo) {
        if (orderTransferService.existsWaitByOrderId(orderInfo.getId())) {
            OrderTransfer orderTransfer = new OrderTransfer();
            orderTransfer.setIsDel(1);
            orderTransferService.deleteByOrderId(orderTransfer, orderInfo.getId());
        }
    }

    /**
     * 进行退款
     *
     * @param source
     * @param refundPrice
     */
    private void refund(String source, int refundPrice, String orderCode) {
        Trade trade = tradeService.infoByOrderCode(orderCode);
        //退款
        WXRefundCommand refundCommand = new WXRefundCommand();
        refundCommand.setTotal(trade.getTotal());
        refundCommand.setRefund(refundPrice);
        refundCommand.setCurrency(trade.getCurrency());
        refundCommand.setOut_refund_no(trade.getTransactionId());
        refundCommand.setOut_trade_no(orderCode);
        try {
            wxPayServiceV3.refund(refundCommand);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 根据id查订单详情
     *
     * @param query id
     * @return 订单
     */
    public MemberOrderDetailDTO detail(MemberOrderDetailsQuery query) {
        OrderService.OrderInfoPlug plug = OrderService.OrderInfoPlug.base();
        plug.setTicket(true);
        plug.setEvaluate(true);
        OrderInfo orderInfo = orderService.getOrderInfoById(query.getId(), plug);
        MemberOrderDetailDTO memberOrderDetailDTO = OrderMapperStruct.INSTANCE.entity2MemberOrderDetailDTO(orderInfo);
        List<Coupon> coupons = couponService.getCouponsByOrderId(query.getId());
        if (CollUtil.isNotEmpty(coupons)) {
            memberOrderDetailDTO.setCouponPrice(orderInfo.getMassageService().getDiscountPrice() * coupons.size());
        }
        OrderTransferCondition condition = new OrderTransferCondition();
        condition.setOrderIds(Lists.newArrayList(query.getId()));
        condition.setAscCreateTime("asc");//todo
        List<OrderTransfer> orderTransfers = orderTransferService.listByCondition(condition);
        if (CollectionUtil.isNotEmpty(orderTransfers)) {
            OrderTransfer orderTransfer = orderTransfers.get(0);
            if (ObjectUtil.isNotNull(orderTransfer)) {
                memberOrderDetailDTO.setMassagistName(orderTransfer.getAppointMassagistName());
                memberOrderDetailDTO.setMassagistId(orderTransfer.getAppointMassagistId());
            }
        }
        return memberOrderDetailDTO;
    }

    public List<MemberAppointmentTimeDTO> appointmentTime(MemberMassagistTimeQuery query) {
        // 如果测试传递了操作时间，则以模拟的操作时间为开始值返回技师可预约时间列表
        LocalDateTime nowTime = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);
        if (Objects.equals(query.getType(), "test") && query.getOpTime() != null) {
            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            nowTime = LocalDateTime.parse(query.getOpTime(), dateTimeFormatter);
        }
        List<Date> dates = getDates(query.getMassagistId(), nowTime);
        return OrderUtils.getOrderTimeList(dates, LocalDateTime.now());
    }

    /**
     * 通过技师id 获取 已经被预约的时间
     *
     * @param massagistId 技师id
     * @return 已经被预约的时间的集合
     */
    public List<Date> getDates(Long massagistId, LocalDateTime nowDateTime) {
        if (nowDateTime == null) {
            nowDateTime = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);
        }
        String nowTime = nowDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        //第二天的23：30：00   变更 24：00：00
        String towTime = nowDateTime.plusDays(1).withHour(23).withMinute(30).withSecond(00).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        //现在到明天的现在的技师订单
        OrderInfoCondition condition = new OrderInfoCondition();
        condition.setMassagistId(massagistId);
        condition.setBeginAppointTime(nowTime);
        condition.setEndAppointTime(towTime);
        condition.setStatusList(Lists.newArrayList(OrderStatusEnum.$20.getCode(), OrderStatusEnum.$30.getCode()));
        List<OrderInfo> orderInfos = orderService.orderList(condition);
        return orderInfos.stream().map(OrderInfo::getAppointTime).toList();
    }

    public WXPayDTO buildPayDto(String sourctType, WXOrderCommand wxPayCommand)  {
        if (sourctType.equals(SourceTypeEnum.WeChat.getCode())) {
            return wxPayServiceV3.jsapi(wxPayCommand, chatConfig.getAppid());
        }
        return null;
    }

    /**
     * 订单支付
     *
     * @author: lig
     * @date: 2023/12/21
     */
    public WXPayDTO pay(OrderPayCommand command) {
        log.info("订单ID {}，订单支付请求参数：{} ", command.getOrderId(), JSONUtil.toJsonStr(command));
        OrderInfo orderInfo = orderService.getOrderInfoById(command.getOrderId(), OrderService.OrderInfoPlug.empty());
        //使用了团购券，支付金额需要减掉
        List<Coupon> coupons = couponService.getCouponsByOrderId(command.getOrderId());
        int couponPrice = 0;
        if (CollUtil.isNotEmpty(coupons)) {
            couponPrice = orderInfo.getMassageService().getDiscountPrice() * coupons.size();
        }
        WXOrderCommand wxPayCommand = new WXOrderCommand();
        if (envUtil.isTest()) {
            //测试 0.1
            wxPayCommand.setTotal(testOrderPrice);
        } else {
            wxPayCommand.setTotal(orderInfo.getOrderPrice() - couponPrice);
        }
        wxPayCommand.setOpenid(command.getOpenId());
        wxPayCommand.setOut_trade_no(orderInfo.getOrderCode());

        WXPayDTO wxPayDTO = buildPayDto(command.getSource(), wxPayCommand);
        if (BeanUtil.isNotEmpty(wxPayDTO)) {
            log.info("订单ID {}， 订单支付返回参数：{} ", command.getOpenId(), JSONUtil.toJsonStr(wxPayDTO));
        }
        return wxPayDTO;

    }

    /**
     * 扩展订单支付
     *
     * @author: lig
     * @date: 2023/12/21
     */
    public WXPayDTO extPay(OrderExtPayCommand command) {
        OrderExt dto = orderService.infoExtOrderById(command.getExtOrderId());
        WXOrderCommand wxPayCommand = new WXOrderCommand();
        if (envUtil.isTest()) {
            //测试 0.01
            wxPayCommand.setTotal(testOrderPrice);
        } else {
            wxPayCommand.setTotal(dto.getOrderPrice());
        }
        wxPayCommand.setOpenid(command.getOpenId());
        wxPayCommand.setOut_trade_no(dto.getOrderExtCode());
        return buildPayDto(command.getSource(), wxPayCommand);
    }

    /**
     * 订单支付成功操作
     *
     * @author: lig
     * @date: 2023/12/20
     */
    public void orderPaySuccess(PaySuccessResult result) {
        //先新增添加支付信息
        tradeService.add(result);

        //修改订单状态
        String orderCode = result.getOutTradeNo();
        OrderInfo orderInfo = orderService.getOrderByCode(orderCode);
        if (BeanUtil.isEmpty(orderInfo)) {
            //处理加单支付成功
            OrderExtCondition extCondition = new OrderExtCondition();
            extCondition.setOrderExtCode(orderCode);
            extCondition.setIsDel(WhetherTypeEnum.$1.getCode());
            OrderExtPO extPO = orderService.infoExtOne(extCondition);
            orderService.ExtOrderPaySuccess(extPO.getId(), WhetherTypeEnum.$0);
        } else {
            List<String> list = new ArrayList<>();
            List<ShopUser> shopUsers = shopUserService.queryListByshopId(orderInfo.getShopId());
            if (CollUtil.isNotEmpty(shopUsers)) {
                shopUsers.stream().forEach(shopUser -> {
                    if (StringUtils.isNotEmpty(shopUser.getOwnerPhone())) {
                        list.add(shopUser.getOwnerPhone());
                    } else {
                        list.add(shopUser.getPhone());
                    }
                });
            }
            //修改订单状态
            OrderInfoPO orderInfoPo = new OrderInfoPO();
            orderInfoPo.setId(orderInfo.getId());
            orderInfoPo.setStatus(OrderStatusEnum.$20.getCode());
            orderInfoPo.setPayTime(new Date());
            orderService.updateOrder(orderInfoPo);
            //技师是否虚拟
            Massagist massagist = massagistService.getInfoByMassagistId(orderInfo.getMassagistId());
            //虚拟技师直接转单
            if (ObjectUtil.equals(massagist.getTag(), 20)) {
                //直接转单给我符合要求的真实技师，如果一个技师也没有，最终还是转单给调度员
                Massagist massagist1 = getRealMassagist(massagist.getShopId(), orderInfo.getAppointTime(), new HashSet<>());
                log.info("Massagist:massagist1 {}", massagist1);
                if (ObjectUtil.isNull(massagist1)) {
                    orderService.orderTransfer(orderInfo, 0L, "system");
                    //语音通知
                    if (smsConfig.isFlagSendVoiceSms()) {
                        List<String> whiteList = sysConfigurationRepositoryService.getValueByKey(KeyConstant.SMS_WHITELIST);
                        list.removeAll(whiteList);
                        list.forEach(phone -> {
                            LinkedHashMap<String, String> templateArgs = new LinkedHashMap<>();
                            templateArgs.put("var1", orderInfo.getOrderCode());
                            try {
                                voiceSenderService.send(phone, templateArgs, "402502");
                            } catch (Exception e) {
                                throw new RuntimeException(e);
                            }
                        });
                    }
                } else {
                    //转单到技师
                    orderService.orderTransfer2Massagist(orderInfo, 0L, "system", massagist1);
                    //语音通知
                    if (smsConfig.isFlagSendVoiceSms()) {
                        LinkedHashMap<String, String> templateArgs = new LinkedHashMap<>();
                        templateArgs.put("var1", orderInfo.getOrderCode());
                        try {
                            voiceSenderService.send(massagist1.getPhone(), templateArgs, "402502");
                        } catch (Exception e) {
                            throw new RuntimeException(e);
                        }
                    }
                    //真实技师发送消息通知技师
                    producer.send(FlowEnum.$PLACE, orderInfo.getId());
                }
            } else {
                //真实技师发送消息通知技师
                producer.send(FlowEnum.$PLACE, orderInfo.getId());
                //语音通知
                if (smsConfig.isFlagSendVoiceSms()) {
                    LinkedHashMap<String, String> templateArgs = new LinkedHashMap<>();
                    templateArgs.put("var1", orderInfo.getOrderCode());
                    try {
                        voiceSenderService.send(orderInfo.getMassagistPhone(), templateArgs, "402502");
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                }
            }
            //发送短信通知调度员
            List<String> whiteList = sysConfigurationRepositoryService.getValueByKey(KeyConstant.SMS_WHITELIST);
            list.removeAll(whiteList);
            smsProvider.sendNotice(list.stream().collect(Collectors.joining(",")), HuaXinUtil.template2(orderInfo.getOrderCode()));
        }
    }

    /**
     * 得到一个符合要求的真实技师
     */
    public Massagist getRealMassagist(long shopId, Date appointTime, Set<String> set) {
        Shop shop = shopService.getShopById(shopId);
        //当门店的自动甩单为关闭时，直接甩给管理员
        if (shop.getFlag() == 0) {
            return null;
        }
        //该城市下的所有真实技师---->变更为该门店下的技师
        List<Massagist> massagists = massagistService.getRealMassagistListByShopId(shopId);
        List<Long> massagistIds = massagists.stream().map(Massagist::getId).toList();
        if (CollUtil.isEmpty(massagists)) {
            return null;
        }
        Massagist result = null;
        List<Long> ids = new ArrayList<>();
        //白名单
        List<String> whiteList = sysConfigurationRepositoryService.getValueByKey(KeyConstant.MASSAGIST_WHITELIST);
        List<String> whiteList4CurrentCity = new ArrayList<>(whiteList.stream().filter(id -> massagistIds.contains(Long.parseLong(id))).toList());
        Collections.shuffle(whiteList4CurrentCity);
        List<Long> sortIds = getLeast(whiteList4CurrentCity);
        log.info("排序后的白名单列表 {}", sortIds);
        for (Long mid : sortIds) {
            if (!ids.contains(mid) && !set.contains(String.valueOf(mid))) {
                Massagist massagist = massagistService.getInfoByMassagistId(mid);
                if (canService2Transfer(massagist, appointTime)) {
                    result = massagist;
                    break;
                }
                ids.add(massagist.getId());
            }
        }
        if (ObjectUtil.isNotNull(result)) {
            return result;
        }
        //查询当月的加钟数和业绩
        LocalDate currentMonth = LocalDate.now();
        String currentMonthFirstDay =
                DateUtil.date2Str(DateUtil.getStartTimeOfCurrentMonth(DateUtil.localDate2Date(currentMonth)));
        String currentMonthLastDay =
                DateUtil.date2Str(DateUtil.getEndTimeOfCurrentMonth(DateUtil.localDate2Date(currentMonth)));

        OrderInfoCondition orderInfoCondition = new OrderInfoCondition();
        orderInfoCondition.setBeginDate(currentMonthFirstDay);
        orderInfoCondition.setEndDate(currentMonthLastDay);
        orderInfoCondition.setMassgistIds(massagistIds);
        List<OrderInfo> orderInfos = orderService.orderList(orderInfoCondition);
        //已完成的订单列表
        List<OrderInfo> inServiceOrders = orderInfos.stream()
                .filter(orderInfo -> Objects.equals(orderInfo.getStatus(), OrderStatusEnum.$40.getCode()))
                .toList();
        //已取消订单（技师已出发或已到达）
        List<OrderInfo> cancel = orderInfos.stream().filter(orderInfo -> Objects.equals(orderInfo.getStatus(), OrderStatusEnum.$99.getCode())
                && (Objects.equals(orderInfo.getOrderOperStatus(), OrderOperStatusEnum.$20.getCode())
                || Objects.equals(orderInfo.getOrderOperStatus(), OrderOperStatusEnum.$30.getCode()))).toList();
        //技师的完成单业绩
        Map<Long, List<Integer>> massagistId2Performance = inServiceOrders.stream()
                .collect(Collectors.groupingBy(OrderInfo::getMassagistId, Collectors.mapping(OrderInfo::getOrderPrice, Collectors.toList())));
        //技师的取消单业绩
        Map<Long, List<Integer>> massagistId2PerformanceCancel = cancel.stream()
                .collect(Collectors.groupingBy(OrderInfo::getMassagistId, Collectors.mapping(OrderInfo::getTravelPrice, Collectors.toList())));
        //技师的加钟数
        List<LeaderBoard> extraTimeLeaderBoard = new ArrayList<>();
        List<LeaderBoard> performanceLeaderBoard = new ArrayList<>();
        Map<Long, List<List<OrderExt>>> massagistId2OrderExts =
                inServiceOrders.stream().collect(Collectors.groupingBy(OrderInfo::getMassagistId, Collectors.mapping(OrderInfo::getOrderExtList, Collectors.toList())));
        massagistId2OrderExts.forEach((id, list) -> {
            LeaderBoard leaderBoard = new LeaderBoard();
            AtomicInteger extCount = new AtomicInteger();
            list.forEach(sublist -> {
                if (ObjectUtil.isNotNull(sublist)) {
                    sublist.forEach(orderExt -> extCount.addAndGet(orderExt.getOrderCount()));
                }
            });
            leaderBoard.setMid(id);
            leaderBoard.setNum(extCount.get());
            if (extCount.get() > 0) {
                extraTimeLeaderBoard.add(leaderBoard);
            }
        });
        Comparator<LeaderBoard> comparator = Comparator.comparing(LeaderBoard::getNum).reversed();
        extraTimeLeaderBoard.sort(comparator);
        massagistIds.forEach(id -> {
            LeaderBoard leaderBoard = new LeaderBoard();
            List<List<OrderExt>> extList = massagistId2OrderExts.get(id);
            AtomicInteger totalPrice = new AtomicInteger();
            if (CollUtil.isNotEmpty(extList)) {
                extList.forEach(sublist -> {
                    if (ObjectUtil.isNotNull(sublist)) {
                        sublist.forEach(orderExt -> totalPrice.addAndGet(orderExt.getOrderPrice()));
                    }
                });
            }
            int cancelPrice = 0;
            if (CollectionUtil.isNotEmpty(massagistId2PerformanceCancel.get(id))) {
                cancelPrice = massagistId2PerformanceCancel.get(id).stream().reduce(Integer::sum).orElse(0);
            }
            int price = 0;
            if (CollUtil.isNotEmpty(massagistId2Performance.get(id))) {
                price = massagistId2Performance.get(id).stream().reduce(Integer::sum).orElse(0);
            }
            leaderBoard.setNum(price + totalPrice.get() + cancelPrice);
            leaderBoard.setMid(id);
            performanceLeaderBoard.add(leaderBoard);
        });
        performanceLeaderBoard.sort(comparator);
        //查询上个月的加钟数
        LocalDate lastMonth = currentMonth.minusMonths(1);
        String lastMonthFirstDay =
                DateUtil.date2Str(DateUtil.getStartTimeOfCurrentMonth(DateUtil.localDate2Date(lastMonth)));
        String lastMonthLastDay =
                DateUtil.date2Str(DateUtil.getEndTimeOfCurrentMonth(DateUtil.localDate2Date(lastMonth)));
        orderInfoCondition.setBeginDate(lastMonthFirstDay);
        orderInfoCondition.setEndDate(lastMonthLastDay);
        orderInfoCondition.setMassgistIds(massagistIds);
        List<OrderInfo> orderInfosLastMonth = orderService.orderList(orderInfoCondition);
        List<LeaderBoard> extraTimeLeaderBoardLastMonth = new ArrayList<>();
        //已完成的订单列表
        List<OrderInfo> inServiceOrdersLastMonth = orderInfosLastMonth.stream()
                .filter(orderInfo -> Objects.equals(orderInfo.getStatus(), OrderStatusEnum.$40.getCode()))
                .toList();
        Map<Long, List<List<OrderExt>>> massagistId2OrderExtsLastMonth =
                inServiceOrdersLastMonth.stream().collect(Collectors.groupingBy(OrderInfo::getMassagistId, Collectors.mapping(OrderInfo::getOrderExtList, Collectors.toList())));
        massagistId2OrderExtsLastMonth.forEach((id, list) -> {
            LeaderBoard leaderBoard = new LeaderBoard();
            AtomicInteger extCount = new AtomicInteger();
            list.forEach(sublist -> {
                if (ObjectUtil.isNotNull(sublist)) {
                    sublist.forEach(orderExt -> extCount.addAndGet(orderExt.getOrderCount()));
                }
            });
            leaderBoard.setMid(id);
            leaderBoard.setNum(extCount.get());
            if (extCount.get() > 0) {
                extraTimeLeaderBoardLastMonth.add(leaderBoard);
            }
        });
        extraTimeLeaderBoardLastMonth.sort(comparator);
        //先按当月的加钟数排序
        log.info("extraTimeLeaderBoard {}", extraTimeLeaderBoard);
        if (ObjectUtil.isNull(result)) {
            for (LeaderBoard board : extraTimeLeaderBoard) {
                if (!ids.contains(board.mid) && !set.contains(String.valueOf(board.mid))) {
                    Massagist massagist = massagistService.getInfoByMassagistId(board.mid);
                    if (canService2Transfer(massagist, appointTime)) {
                        result = massagist;
                        break;
                    }
                    ids.add(massagist.getId());
                }
            }
        }
        //再按上个月的加钟数排序
        log.info("extraTimeLeaderBoardLastMonth {}", extraTimeLeaderBoardLastMonth);
        if (ObjectUtil.isNull(result)) {
            for (LeaderBoard board : extraTimeLeaderBoardLastMonth) {
                if (!ids.contains(board.mid) && !set.contains(String.valueOf(board.mid))) {
                    Massagist massagist = massagistService.getInfoByMassagistId(board.mid);
                    if (canService2Transfer(massagist, appointTime)) {
                        result = massagist;
                        break;
                    }
                    ids.add(massagist.getId());
                }
            }
        }
        //再按当月的业绩排序
        log.info("performanceLeaderBoard {}", performanceLeaderBoard);
        if (ObjectUtil.isNull(result)) {
            for (LeaderBoard leaderBoard : performanceLeaderBoard) {
                if (!ids.contains(leaderBoard.mid) && !set.contains(String.valueOf(leaderBoard.mid))) {
                    Massagist massagist = massagistService.getInfoByMassagistId(leaderBoard.mid);
                    if (canService2Transfer(massagist, appointTime)) {
                        result = massagist;
                        break;
                    }
                    ids.add(massagist.getId());
                }
            }
        }
        return result;
    }

    /**
     * 按被派单的数量排序，少的在前
     */
    private List<Long> getLeast(List<String> whiteList) {
        log.info("配置的白名单为 ： {}", whiteList);
        LocalDate today = LocalDate.now();
        String beginTime = today.atStartOfDay().format(DateTimeFormatter.ofPattern(DateUtil.YMD_HMS));
        String endTime = today.plusDays(1).atStartOfDay().format(DateTimeFormatter.ofPattern(DateUtil.YMD_HMS));
        List<Long> ids = whiteList.stream().map(Long::parseLong).toList();
        //查询到今天白名单技师完成或进行中的订单
        OrderInfoCondition orderInfoCondition = new OrderInfoCondition();
        orderInfoCondition.setBeginDate(beginTime);
        orderInfoCondition.setEndDate(endTime);
        orderInfoCondition.setMassgistIds(ids);
        orderInfoCondition.setStatusList(Lists.newArrayList(OrderStatusEnum.$30.getCode(), OrderStatusEnum.$40.getCode()));
        List<OrderInfo> orderInfos = orderService.orderList(orderInfoCondition);
        if (CollUtil.isEmpty(orderInfos)) {
            return ids;
        }
        Map<Long, List<OrderInfo>> map0 = orderInfos.stream()
                .collect(Collectors.groupingBy(OrderInfo::getMassagistId, Collectors.mapping(Function.identity(), Collectors.toList())));
        //查询订单的转单记录
        List<Long> orderIds = orderInfos.stream().map(OrderInfo::getId).toList();
        OrderTransferCondition condition = new OrderTransferCondition();
        condition.setOrderIds(orderIds);
        condition.setAscCreateTime("asc");//todo
        List<OrderTransfer> orderTransfers = orderTransferService.listByCondition(condition);
        Map<Long, OrderTransfer> map = orderTransfers.stream().collect(Collectors.toMap(OrderTransfer::getOrderId, Function.identity(), (k1, k2) -> k1));
        //得到一个key是技师id value是转单次数的map
        Map<Long, Integer> map1 = new HashMap<>();
        ids.forEach(id -> {
            List<OrderInfo> orders = map0.get(id);
            if (CollUtil.isNotEmpty(orders)) {
                orders.forEach(orderInfo -> {
                    OrderTransfer orderTransfer = map.getOrDefault(orderInfo.getId(), null);
                    if (ObjectUtil.isNotEmpty(orderTransfer)) {
                        map1.put(id, map1.getOrDefault(id, 0) + 1);
                    } else {
                        map1.put(id, map1.getOrDefault(id, 0));
                    }
                });
            } else {
                map1.put(id, map1.getOrDefault(id, 0));
            }
        });
        log.info("白名单转单次数map ： {}", map1);
        //按照value排序
        List<Long> sortedMap = map1.entrySet().stream()
                .sorted(Map.Entry.comparingByValue())
                .map(Map.Entry::getKey)
                .toList();
        return sortedMap;
    }

    /**
     * 如果技师可以接单，就进行转单
     * @return
     */
    private boolean canService2Transfer(Massagist massagist, Date appointTime) {
        //黑名单直接返回
        if (sysConfigurationRepositoryService.getValueByKey(KeyConstant.MASSAGIST_BLACKLIST).contains(String.valueOf(massagist.getId()))) {
            return false;
        }
        List<LocalDateTime> localDateTimes = OrderUtils.getAvailableTimeList(getDates(massagist.getId(), LocalDateTime.of(LocalDate.now(), LocalTime.MIN)));
        return OrderUtils.contains(localDateTimes, OrderUtils.toLocalDateTime(appointTime))
                && (Objects.equals(massagist.getStatus(), MassagistStatusEnum.$20.getCode()) ||
                Objects.equals(massagist.getStatus(), MassagistStatusEnum.$30.getCode()));
    }

    /**
     * @description: 前置校验
     * @author: lig
     * @date: 2024/3/14
     */
    public void precheckPrecontract(MemberEnterOrderPrecontractCommand command) {
        //门店是否上架校验
        if (!massagistService.verifyUsable(command.getMassagistId())) {
            throw new SpaException(CommonCode.HINT_REFRESH.getCode(), "您来晚了，该技师已休息，请选择其他技师预约");
        }

    }

    @Data
    public static class LeaderBoard {

        private Long mid;

        private Integer num;
    }

}
